package mobilefeel.accelerometer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import mobilefeel.AndroidDevice;
import mobilefeel.DeviceDispatcher;
import mobilefeel.NotConnectedException;
import mobilefeel.dto.AccelerometerEvent;
import mobilefeel.dto.AccelerometerRate;
import mobilefeel.dto.DataTransferObject;

/**
 * Represents an Android device's accelerometer. Accelerometer allows the
 * developer to manage {@link AccelerometerListener}'s and modify accelerometer
 * event parameters.
 */
public class Accelerometer {

	/**
	 * Get accelerometer data as fast as possible.
	 */
	public static final int SENSOR_DELAY_FASTEST = 0;
	/**
	 * Get accelerometer data at a rate suitable for games.
	 */
	public static final int SENSOR_DELAY_GAME = 1;
	/**
	 * Get accelerometer data at a rate suitable for the user interface. This
	 * is the default value.
	 */
	public static final int SENSOR_DELAY_UI = 2;
	/**
	 * Get accelerometer data at a rate suitable for screen orientation
	 * changes.
	 */
	public static final int SENSOR_DELAY_NORMAL = 3;
	
	private AndroidDevice android;
	private List<AccelerometerListener> listeners;
	
	private Accelerometer(AndroidDevice androidDevice){
		android = androidDevice;
		listeners = new ArrayList<AccelerometerListener>();
	}
	
	/**
	 * Returns the Accelerometer instance linked to the {@link AndroidDevice}.
	 * 
	 * @param androidDevice the desired {@link AndroidDevice} (cannot be null)
	 * @return an Accelerometer instance representing the device's
	 * 			accelerometer
	 * @see AndroidDevice#getAccelerometer()
	 */
	public static Accelerometer getInstance(AndroidDevice androidDevice) {
		if (androidDevice == null)
			throw new IllegalArgumentException("androidDevice cannot be null.");
		
		if (androidDevice.getAccelerometer() == null)
			return new Accelerometer(androidDevice);
		return androidDevice.getAccelerometer();
	}
	
	/**
	 * Registers a callback to be invoked when an Android device performs an
	 * accelerometer event.
	 * 
	 * @param listener the {@link AccelerometerListener} that will be notified
	 * of accelerometer events (cannot be null)
	 * @throws NotConnectedException if the device is not connected
	 * @throws IOException if a network error occurs while communicating with
	 * the device
	 * @see #removeAccelerometerListener(AccelerometerListener)
	 */
	public void addAccelerometerListener(AccelerometerListener listener) throws NotConnectedException, IOException {
		if (listener == null)
			throw new IllegalArgumentException("listener cannot be null.");
		if (listeners.size() == 0) {
			DataTransferObject dto = new DataTransferObject();
			dto.setType(DataTransferObject.ACCELEROMETER_REGISTER_EVENT);
			DeviceDispatcher.sendObject(android, dto);
		}
		listeners.add(listener);
	}
	
	/**
	 * Unregisters an accelerometer events listener.
	 * 
	 * @param listener the {@link AccelerometerListener} that will not be 
	 * notified of accelerometer events anymore
	 * @throws NotConnectedException if the device is not connected
	 * @throws IOException if a network error occurs while communicating with
	 * the device
	 * @see #addAccelerometerListener(AccelerometerListener)
	 */
	public void removeAccelerometerListener(AccelerometerListener listener) throws NotConnectedException, IOException {
		listeners.remove(listener);
		if (listeners.size() == 0) {
			DataTransferObject dto = new DataTransferObject();
			dto.setType(DataTransferObject.ACCELEROMETER_UNREGISTER_EVENT);
			DeviceDispatcher.sendObject(android, dto);
		}
	}
	
	synchronized void notifyAccelerometerListeners(AccelerometerEvent e) {
		for (AccelerometerListener listener : this.listeners)
			listener.onSensorChanged(android, e);
	}
	
	/**
	 * Sets the Android device's accelerometer to the specified event rate. 
	 * 
	 * @param rate rate of events. This is only a hint to the system. events
	 * may be received faster or slower than the specified rate. Usually
	 * events are received faster. The value must be one of {@link
	 * #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI} (default), {@link
	 * #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}
	 * @throws NotConnectedException if the device is not connected
	 * @throws IOException if a network error occurs while communicating with
	 * the device
	 */
	public void setAccelerometerRate(int rate) throws NotConnectedException, IOException{
		AccelerometerRate accelerometerRate = new AccelerometerRate();
		accelerometerRate.setType(DataTransferObject.ACCELEROMETER_RATE_SET);
		accelerometerRate.setRate(rate);
		DeviceDispatcher.sendObject(android, accelerometerRate);
	}
	
}
